﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Runtime.InteropServices;

namespace StreamingServer
{
    public partial class Form1 : Form
    {
        #region 变量

        IntPtr ChannelHandle;

        #endregion

        //将委托声明为成员变量！！
        STREAM_DIRECT_READ_CALLBACK sdrc;


        #region 用于录像
        //用于存放头文件
        byte[] FileHeader;
        //文件头长度
        int FileHeaderLen;
        //是否开始捕获文件 0 未启用 1 启用
        volatile int CaptureState;
        #endregion
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            //设置系统默认的视频制式
            HikVisionSDK.SetDefaultVideoStandard(VideoStandard_t.StandardNTSC);
            //初始化板卡
            //初始化板卡
            if (HikVisionSDK.InitDSPs() < 0)
            {
                MessageBox.Show("初始化DSPs失败！！");
                return;
            }

            if (HikVisionSDK.GetTotalDSPs() == 0)
            {
                MessageBox.Show("没有可用的通道！！您是否已经启动服务器端？");
                return;
            }

            //打开通道
            ChannelHandle = HikVisionSDK.ChannelOpen(0);
            //设置编码帧结构、帧率
            HikVisionSDK.SetIBPMode(ChannelHandle, 100, 2, 1, 25);
            //设置编码图像质量
            HikVisionSDK.SetDefaultQuant(ChannelHandle, 15, 15, 20);

            //视频预览
           //StartVideoPreview();
            PreviewAndClientConnect();

        }

        /// <summary>
        /// 视频预览
        /// </summary>

        private void StartVideoPreview()
        {
            Rectangle rect = panel1.ClientRectangle;
            HikVisionSDK.StartVideoPreview(ChannelHandle, panel1.Handle, ref rect, false, (int)TypeVideoFormat.vdfRGB16, 25);
        }

        /// <summary>
        /// 窗体移动
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Form1_Move(object sender, EventArgs e)
        {
            HikVisionSDK.StopVideoPreview(ChannelHandle);
            StartVideoPreview();

        }
        /// <summary>
        /// 预览并客户端连接
        /// </summary>
        private void PreviewAndClientConnect()
        {

            sdrc = new STREAM_DIRECT_READ_CALLBACK(STREAM_DIRECT_READ_CALLBACK1);

            //[必须]注册编码图像数据流直接读取回调函数
            HikVisionSDK.RegisterStreamDirectReadCallback(sdrc, this.Handle);

            //[必须]启动服务端
            HikServerDLL.MP4_ServerSetStart(new StartCap(StartCap));
            //HikServer.MP4_ServerSetStop(sc);

            //HikServer.MP4_ServerSetIBPMode(new SetIBP(SetIBP));
            //[必须]设置回调，重新生成一个I帧
            HikServerDLL.MP4_ServerSetCapIFrame(new MakeIFrame(MakeIFrame));

            //HikServer.MP4_ServerSetTTL(64);
            //HikServer.MP4_ServerSetNetPort(5050, 6050);

            PSERVER_VIDEOINFO videoInfo = new PSERVER_VIDEOINFO();
            //初始化
            videoInfo.m_datatype = new byte[64];
            //设置发送缓冲区大小
            HikServerDLL.MP4_ServerSetBufNum((ushort)0, (ushort)90);
            videoInfo.m_datatype[0] = (byte)ChannelDataType.SMALLPIC;
            videoInfo.m_channum = (byte)1;
            videoInfo.m_waittime = 5;

            //设置每个通道的最大用户数量
            //HikServer.MP4_ServerMaxUser(0, 24);

            if (HikServerDLL.MP4_ServerStart(ref videoInfo) == 0)
            {
                MessageBox.Show("服务端启动错误！！");
            }

            //开启视频预览
            StartVideoPreview();
        }



        #region 回调函数

        public void StartCap(int port)
        {
            HikVisionSDK.StartVideoCapture(ChannelHandle);
        }

        public void MakeIFrame(int port)
        {
            HikVisionSDK.CaptureIFrame(ChannelHandle);
        }

        //public int STREAM_DIRECT_READ_CALLBACK1(int channelNum, IntPtr DataBuf, int Length, FrameType_t frameType, IntPtr context)
        //{
        //    int status = 0;
        //    HikServerDLL.MP4_ServerWriteDataEx(channelNum, DataBuf, Length, (int)frameType, status, 0);
        //    return 0;
        //}
        #endregion

        private void btnGetImage_Click(object sender, EventArgs e)
        {
            byte[] imageBuf = new byte[704 ^ 576 * 2];
            int size = 704 * 575 * 2;
            HikVisionSDK.GetJpegImage(ChannelHandle, imageBuf, out size, 100);
            using (MemoryStream ms = new MemoryStream(imageBuf))//IO输入输出
            {
                Image image = Image.FromStream(ms, true);
                image.Save("D:\\1.jpg");
            }
            /*
             * 注意GetJpegImage的参数说明！！并且请注意，由于这个示例，发现前面的(GetJpegImage/GetOriginalImage)API错误了，请你及时更新！！

            public static extern int GetOriginalImage(IntPtr hChannelHandle, byte[] ImageBuf, out int Size);

            public static extern int GetJpegImage(IntPtr hChannelHandle, byte[] ImageBuf, out int Size, uint nQuality);

          保存为bmp的方法请自行尝试，应该是差不多的: ) 


             */
        }

        /// <summary>
        /// 开始录像
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnReacode_Click(object sender, EventArgs e)
        {
            //写入头文件
            using (FileStream fs = new FileStream("D:\\hik.264", FileMode.Create))
            {
                BinaryWriter bw = new BinaryWriter(fs);
                bw.Write(FileHeader);
                bw.Flush();
                bw.Close();
            }

            CaptureState = 1;
        }

        private void btnStopReacode_Click(object sender, EventArgs e)
        {
            uint endCode = 0x00000002;
            CaptureState = 0;
            using (FileStream fs = new FileStream("C:\\hik.264", FileMode.Append))
            {
                BinaryWriter bw = new BinaryWriter(fs);
                bw.Write(endCode);
                bw.Close();
            }
        }
        public int STREAM_DIRECT_READ_CALLBACK1(int channelNum, IntPtr DataBuf, int Length, FrameType_t frameType, IntPtr context)
        {
            //int status = 0;
            //HikServer.MP4_ServerWriteDataEx(channelNum, DataBuf, Length, (int)frameType, status, 0);
            //return 0;

            int status = 0;

            if (frameType > 0)
            {
                if (frameType == FrameType_t.PktSysHeader)
                {
                    FileHeader = new byte[Length];
                    Marshal.Copy(DataBuf, FileHeader, 0, Length);
                    FileHeaderLen = Length;
                }

                if (frameType == FrameType_t.PktIFrames || frameType == FrameType_t.PktSubIFrames)
                    status = 1;
                else
                    status = 0;

                if (frameType == FrameType_t.PktMotionDetection || frameType == FrameType_t.PktOrigImage)
                    return 0;
            }

            if (Length == 0)
            {
                //TRACE("no data ?\n");
                return 0;
            }

            if (frameType == FrameType_t.PktAudioFrames)
            {
                WriterVideoCapture(Length, DataBuf);
                //写文件
                //    _write(gFileHandleQcif[channelNum],DataBuf,Length);
                //HikServer.MP4_ServerWriteDataEx(channelNum, DataBuf, Length, (int)frameType, status, 1);
                //    _write(gFileHandle[channelNum], DataBuf, Length);
                HikServerDLL.MP4_ServerWriteDataEx(channelNum, DataBuf, Length, (int)frameType, status, 0);
            }
            else if (frameType == FrameType_t.PktSubIFrames || frameType == FrameType_t.PktSubPFrames || frameType == FrameType_t.PktSubBBPFrames || frameType == FrameType_t.PktSubSysHeader)
            {
                //    _write(gFileHandleQcif[channelNum],DataBuf,Length);
                HikServerDLL.MP4_ServerWriteDataEx(channelNum, DataBuf, Length, (int)frameType, status, 1);
            }
            else
            {
                WriterVideoCapture(Length, DataBuf);
                HikServerDLL.MP4_ServerWriteDataEx(channelNum, DataBuf, Length, (int)frameType, status, 0);
            }

            return 0;

        }
        /// <summary>
        /// 将数据流写入视频文件
        /// </summary>
        /// <param name="length"></param>
        /// <param name="dataBuf"></param>
        private void WriterVideoCapture(int length, IntPtr dataBuf)
        {
            if (CaptureState == 1)
            {
                using (FileStream fs = new FileStream("C:\\hik.264", FileMode.Append))
                {
                    BinaryWriter bw = new BinaryWriter(fs);

                    byte[] byteBuf = new byte[length];
                    Marshal.Copy(dataBuf, byteBuf, 0, length);
                    bw.Write(byteBuf);
                    bw.Flush();
                    bw.Close();
                }
            }
        }
     /*   暂时有错误，不能使用
        /// <summary>
        /// 全屏显示视频窗口
        /// </summary>
        /// <param name="tsmFullScreen">右键</param>
        /// <param name="pVideo">显示视频的窗口</param>
        /// <param name="cs">视频连接成功返回的值</param>
        private void FullScreen(ToolStripMenuItem tsmFullScreen, Panel pVideo, int cs)
        {
            if (this.WindowState == System.Windows.Forms.FormWindowState.Maximized)
            {
                tsmFullScreen.Text = "全屏窗口";
                this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
                this.WindowState = System.Windows.Forms.FormWindowState.Normal;
                this.TopMost = false;
                pVideo.Width = pre_Width;
                pVideo.Height = pre_Height;
                pVideo.Top = pre_Top;
                pVideo.Left = pre_Left;

                //隐藏其他控件
                HideOtherControls(pVideo, true);

                minPanel(pVideo, cs);
            }
            else
            {
                tsmFullScreen.Text = "关闭全屏";
                this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
                this.WindowState = System.Windows.Forms.FormWindowState.Maximized;
                this.TopMost = true;

                pre_Width = pVideo.Width;
                pre_Height = pVideo.Height;
                pre_Top = pVideo.Top;
                pre_Left = pVideo.Left;

                pVideo.Width = this.Width;
                pVideo.Height = this.Height;
                pVideo.Top = 0;
                pVideo.Left = 0;

                //显示其他控件
                HideOtherControls(pVideo, false);
            }
        }

        */
    }
}
/*
 *    代码说明：

               1.     将Form2_Load中最后一行代码StartVideoPreview替换成PreviewAndClientConnect调用即可。

               2.     调用注释前面带了"[必须]"的方法是必须调用的，而被我的注释掉的方法参照源代码可以加也可以不加，因为他是有默认设置的。

               3.     MakeIFrame这个回调函数是客户端连接服务器的关键，如果没有执行这个回调客户端将不能够连接并显示画面！

               4.     STREAM_DIRECT_READ_CALLBACK1回调函数在VC++代码说明里面已经说明了，因为本章不写视频存储，所以把其他代码都注释掉了，只管往内存写数据就行了。

 

注意

     1.     StartVideoPreview的参数用结构体RECT会报错，直接使用Rectangle结构体即可。

     2.     使用GetDspCount总是只返回可用的Dsp数量，而用GetTotalDSPs可以获取所有的Dsp数量。

     3.     再强调一遍，虽然我这里没有把委托实例化成 成员变量，也能调试通过，但是强烈建议您把这些都写成 成员变量然后在窗体初始化时初始化！

     4.     本文是后续服务器端文章的基础，务必细心调试，我敢说如果本文的功能你达到了——你的服务器端可以说完成了60%！！

 

修改记录

     1.     2009-3-30

          将STREAM_DIRECT_READ_CALLBACK声明为成员变量，发现不声明成成员变量在VS里面调试可以运行通过(有时候)，但是直接运行exe文件会报内存出错！！
 */